package com.geeguo.ebuilder.operation.business.system.webmodule.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.ObjectEqualsHelper;
import com.geeguo.ebuilder.core.common.utils.key.UUIDCheckUtils;
import com.geeguo.ebuilder.core.redis.constants.CacheTimeConstants;
import com.geeguo.ebuilder.operation.base.redis.utils.CacheKeyFactory;
import com.geeguo.ebuilder.operation.base.security.model.LoginUser;
import com.geeguo.ebuilder.operation.business.system.rolewebmodule.service.RoleWebModuleService;
import com.geeguo.ebuilder.operation.business.system.webmodule.mapper.WebModuleMapper;
import com.geeguo.ebuilder.operation.business.system.webmodule.model.*;
import com.geeguo.ebuilder.operation.business.system.webmodule.service.WebModuleService;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class WebModuleServiceImpl extends ServiceImpl<WebModuleMapper, WebModuleEntity> implements WebModuleService {

    @Autowired
    private WebModuleMapper webModuleMapper;
    @Autowired
    private RedisCacheService redisCacheService;
    @Autowired
    private RoleWebModuleService roleWebModuleService;

    @Override
    public List<WebModuleVO> listAll() {
        List<WebModuleVO> result = new ArrayList<>();
        List<WebModuleEntity> list = webModuleMapper.listAll();
        if (list != null && !list.isEmpty()) {
            for (WebModuleEntity item : list) {
                result.add(WebModuleConverter.INSTANCE.convert2VO(item));
            }
        }
        return result;
    }
    
    @Override
    public List<WebModuleVO> listAllTree() {
        List<WebModuleVO> result = new ArrayList<>();
        List<WebModuleEntity> list = webModuleMapper.listAll();
        if (list != null && !list.isEmpty()) {
            Map<String, WebModuleVO> mapping = new HashMap<>();
            for (WebModuleEntity item : list) {
                WebModuleVO vo = WebModuleConverter.INSTANCE.convert2VO(item);
                if (UUIDCheckUtils.check(vo.getParentId())) {
                    WebModuleVO parent = mapping.get(vo.getParentId());
                    if (parent != null) {
                        parent.addChild(vo);
                    }
                } else {
                    result.add(vo);
                }
                mapping.put(vo.getId(), vo);
            }
        }
        return result;
    }

    @Override
    public List<WebModuleVO> listByParent(String parentId) {
        LambdaQueryWrapper<WebModuleEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WebModuleEntity::getParentId, parentId);
        queryWrapper.orderByAsc(WebModuleEntity::getSequence, WebModuleEntity::getModifyTime);
        List<WebModuleEntity> moduleList = webModuleMapper.selectList(queryWrapper);
        return WebModuleConverter.INSTANCE.convertEntityList2VOList(moduleList);
    }

    @Override
    public List<WebModuleVO> listByUser(LoginUser loginUser) {
        List<WebModuleVO> result = null;
        String cacheKey = CacheKeyFactory.INSTANCE.getUserWebModuleList(loginUser.getUserId());
        String cacheJson = redisCacheService.get(cacheKey);
        if (StringUtils.isNotEmpty(cacheJson)) {
            List<WebModuleCO> list = GsonUtils.fromJson(cacheJson, new TypeToken<>(){});
            result = WebModuleConverter.INSTANCE.convertCOList2VOList(list);
        }
        if (result == null) {
            result = new ArrayList<>();
            List<WebModuleVO> allList = listAll();
            if (loginUser.isSuperAdmin()) {
                result.addAll(allList);
            } else {
                List<String> userModuleIds = roleWebModuleService.listModuleIdByUser(loginUser.getUserId());
                if (CollectionUtils.isNotEmpty(userModuleIds)) {
                    for (WebModuleVO item : allList) {
                        for (String moduleId : userModuleIds) {
                            if (item.getId().equals(moduleId)) {
                                result.add(item);
                                break;
                            }
                        }
                    }
                }
            }
            cacheJson = GsonUtils.toJson(WebModuleConverter.INSTANCE.convertVOList2COList(result));
            redisCacheService.set(cacheKey, cacheJson, CacheTimeConstants.CACHE_HOUR);
        }
        return result;
    }

    @Override
    public Set<String> listPermissionCodeByUser(LoginUser loginUser) {
        Set<String> result = new HashSet<>();
        List<WebModuleVO> moduleList = listByUser(loginUser);
        if (moduleList != null && !moduleList.isEmpty()) {
            for (WebModuleVO item : moduleList) {
                if (StringUtils.isNotEmpty(item.getPermissionCode())) {
                    result.add(item.getPermissionCode());
                }
            }
        }
        return result;
    }

    @Override
    public WebModuleVO get(String id) {
        WebModuleEntity entity = webModuleMapper.selectById(id);
        if (entity != null) {
            return WebModuleConverter.INSTANCE.convert2VO(entity);
        }
        return null;
    }

    @Override
    public WebModuleCO getCached(String id) {
        WebModuleCO result = null;
        String cacheKey = CacheKeyFactory.INSTANCE.getWebModule(id);
        String cacheJson = redisCacheService.get(cacheKey);
        if (StringUtils.isNotEmpty(cacheJson)) {
            result = GsonUtils.fromJson(cacheJson, WebModuleCO.class);
        }
        if (result == null) {
            WebModuleVO webModule = this.get(id);
            if (webModule != null) {
                result = WebModuleConverter.INSTANCE.convert2CO(webModule);
                cacheJson = GsonUtils.toJson(result);
                redisCacheService.set(cacheKey, cacheJson, CacheTimeConstants.CACHE_WEEK);
            }
        }
        return result;
    }

    @Override
    public String getFullPath(String id) {
        String result = null;
        WebModuleCO current = getCached(id);
        if (current != null) {
            if (current.getDepth() == 1) {
                result = current.getTitle();
            }
            if (current.getDepth() == 2) {
                WebModuleCO levelOne = getCached(current.getParentId());
                if (levelOne != null) {
                    result = String.format("%s-%s", levelOne.getTitle(), current.getTitle());
                }
            }
            if (current.getDepth() == 3) {
                WebModuleCO levelTow = getCached(current.getParentId());
                if (levelTow != null) {
                    WebModuleCO levelOne = getCached(levelTow.getParentId());
                    if (levelOne != null) {
                        result = String.format("%s-%s-%s", levelOne.getTitle(), levelTow.getTitle(), current.getTitle());
                    }
                }
            }
        }
        return result;
    }

    @Override
    public int getMaxSequence(String id) {
        Integer result = webModuleMapper.getMaxSequence(id);
        return result != null ? result : 0;
    }

    @Override
    public boolean save(WebModuleEntity entity) {
        return webModuleMapper.insert(entity) > 0;
    }

    @Override
    @Transactional
    public boolean update(WebModuleEntity current, WebModuleEntity old) {
        Map<String, Object> changeValues = ObjectEqualsHelper.equals(current, old, new String[] {
            "parentId", "name", "title", "type", "sequence", "icon", "path", "component", "hidden", "permissionCode"
        });
        if (changeValues.isEmpty()) {
            return true;
        } else {
            if (changeValues.get("parentId") != null) {
                if (changeValues.get("parentId").equals("0")) {
                    changeValues.put("depth", 1);
                } else {
                    WebModuleVO parent = this.get(changeValues.get("parentId").toString());
                    if (parent != null) {
                        changeValues.put("depth", parent.getDepth() + 1);
                    }
                }
            }
            changeValues.put("id", current.getId());
            changeValues.put("modifyTime", current.getModifyTime());
            changeValues.put("modifyUser", current.getModifyUser());
            boolean result = webModuleMapper.updateByParams(changeValues) == 1;
            orderSequence(old, changeValues);
            if (result) {
                removeCached(current.getId());
            }
            return result;
        }
    }

    @Override
    public boolean remove(String id) {
        boolean result = webModuleMapper.deleteById(id) == 1;
        if (result) {
            removeCached(id);
        }
        return result;
    }

    @Override
    public boolean existsChild(String id) {
        Integer result = webModuleMapper.getChildCount(id);
        return result > 0;
    }

    @Override
    public boolean existsName(String name) {
        Integer result = webModuleMapper.existsName(name);
        return result != null;
    }

    @Override
    public void removeCachedUserWebModules(LoginUser loginUser) {
        redisCacheService.del(CacheKeyFactory.INSTANCE.getUserWebModuleList(loginUser.getUserId()));
    }

    private void removeCached(String id) {
        String cacheKey = CacheKeyFactory.INSTANCE.getWebModule(id);
        redisCacheService.del(cacheKey);
    }

    private void orderSequence(WebModuleEntity old, Map<String, Object> changeValues) {
        if (changeValues.get("sequence") != null) {
            String parentId = changeValues.get("parentId") != null ? changeValues.get("parentId").toString() : old.getParentId();
            List<WebModuleVO> moduleVOList = this.listByParent(parentId);
            List<WebModuleEntity> updateList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(moduleVOList)) {
                int tempSeq = 1;
                for (WebModuleVO moduleVO : moduleVOList) {
                    WebModuleEntity update = new WebModuleEntity();
                    update.setId(moduleVO.getId());
                    update.setSequence(tempSeq);
                    updateList.add(update);
                    tempSeq++;
                }
                super.updateBatchById(updateList);
            }
        }
    }
}